﻿using System;
using System.Security.Cryptography;
using System.Text;
using WSKnowledge.WSHelpers.DesignByContract;
using System.Diagnostics;

namespace WSKnowledge.WSHelpers
{
	public static class Hashing
	{
		public static string GenerateToken(int tokenSize)
		{
			Check.Require(tokenSize > 0);

			int increment = 0;
			string result;

			do
			{
				byte[] buff = new byte[tokenSize + increment++];
				new RNGCryptoServiceProvider().GetBytes(buff);
				result = Convert.ToBase64String(buff);
				Debug.WriteLine(String.Format("GenerateToken: Round #{0}", increment));
				Debug.WriteLine(String.Format("GenerateToken: generated string length #{0}", result.Length));
			} while (result.IsLessThan(tokenSize));

			result = result.Substring(0, tokenSize);
			Check.Ensure(result.IsExactly(tokenSize), "Generated random string does not have the correct tokenSize");
			return result;
		}

		public static string GetHash(string plainText)
		{
			Check.Require(plainText.IsNotEmpty());

			SHA512Managed sha512 = new SHA512Managed();
			byte[] data = Encoding.Unicode.GetBytes(plainText);
			byte[] result = sha512.ComputeHash(data);
			sha512.Clear();

			string hashed = Encoding.Unicode.GetString(result);
			Check.Ensure(hashed.Length == 32, "Hashed value length is not as expected");
			return hashed;
		}

		public static bool ValidateHash(string hashed, string plainText)
		{
			Check.Require(hashed.IsNotEmpty());
			Check.Require(plainText.IsNotEmpty());

			string computed = GetHash(plainText);
			return (hashed == computed);
		}

		/// <summary>
		/// RS Hash Function is a simple hash function from Robert Sedgwicks Algorithms in C book.
		/// I've added some simple optimizations to the algorithm in order to speed up its hashing process.
		/// from: www.partow.net
		/// </summary>
		/// <param name="input">array of objects, parameters combination that you need to get a unique hash code for them</param>
		/// <returns>Hash code</returns>
		public static int RSHash(params object[] input)
		{
			const int b = 378551;
			int a = 63689;
			int hash = 0;

			// I have added the unchecked keyword to make sure not 
			// to get an over flow exception.

			unchecked
			{
				for (int i = 0; i < input.Length; i++)
				{
					hash = hash * a + input[i].GetHashCode();
					a = a * b;
				}
			}

			return hash;
		}
	}
}
